<template>
  <div class="dataList" :class="{smallPadding: pure, noPadding}">
    <!-- 列表顶部 -->
    <el-row :gutter="30" type="flex" justify="space-between" class="listTop" v-if="showListTop">
      <!-- 搜索组件 -->
      <el-col :span="8" class="searchPanel" v-if="listLayout.includes('search')">
        <div v-if="filterType === 'all' || filterData.length == 0">
          <slot name="search">
            <el-input :placeholder="placeholder" v-model.trim="searchValue" @change="searchByKeywords" maxlength="10" clearable @clear="handleClear">
              <i slot="prefix" class="el-input__icon el-icon-search"></i>
            </el-input>
          </slot>
        </div>
      </el-col>
      <!-- 右侧按钮组 -->
      <el-col :span="menuSpan" class="btnList" :class="!listLayout.includes('search') && 'flex-full'" v-if="listLayout.includes('menu')">
        <slot name="buttons">
          <el-button
            size="small"
            v-for="(item, index) in menu"
            :key="item.name"
            :type="(index + 1) % 2 == 0 ? 'default' : 'primary'"
            :plain="(index + 1) % 3 == 0"
            @click="menuClick(item)"
          >{{item.label}}</el-button>
        </slot>
      </el-col>
    </el-row>

    <!-- 列表筛选[默认显示2条，按钮点击显示全部] -->
    <el-row :gutter="40" :class="`listFilter ${openFilter ? 'open':''}`" v-if="filterData.length && !pure && filterType === 'all'">
      <el-col :span="filterSpan" v-for="(item, index) in filterData" :key="index">
        <filterInput :name="item.filterName" :ref="`filterItem${index}`" v-bind="item" />
      </el-col>
      <el-col :span="filterSpan">
        <el-button type="primary" size="small" @click="handleFilter">查询</el-button>
        <el-button size="small" @click="resetFilter">重置</el-button>
        <el-button type="text" @click="openFilter = !openFilter" v-if="filterData.length > 2">
          {{openFilter ? '收起':'展开'}}
          <i :class="`el-icon-arrow-${openFilter?'up':'down'} el-icon--right`" />
        </el-button>
      </el-col>
    </el-row>
    <!-- 列表筛选[默认只显示一个按钮，点击弹窗筛选]  -->
    <el-row :gutter="40" :class="`listFilter ${openFilter ? 'open':''}`" v-if="filterData.length && !pure && filterType === 'default'">
      <!-- 选中的筛选 -->
      <el-col :span="24 - filterSpan">
        <div class="chooseFilter">
          <span>筛选：</span>
          <ul v-if="choosedFilterList.length">
            <li v-for="(item, index) in choosedFilterList" :key="index" style="width: 50%">
              <filterInput
                :key="item.filterName"
                :ref="`chooseFilterItem${index}`"
                v-bind="item"
                binding
                v-model="item.value"
                @input="handleFilterInputChange($event, item)"
              />
            </li>
            <li>
              <el-button type="primary" size="small" @click="handleSecFilter">查询</el-button>
            </li>
          </ul>
        </div>
      </el-col>

      <el-col :span="filterSpan">
        <div class="cfilterRight">
          <!-- 文字搜索框 -->
          <div class="searchPanel" v-if="listLayout.includes('search')">
            <slot name="search">
              <el-input :placeholder="placeholder" v-model="searchValue" @change="searchByKeywords" clearable @clear="handleClear">
                <i slot="prefix" class="el-input__icon el-icon-search"></i>
              </el-input>
            </slot>
          </div>
          <!-- 筛选弹窗 -->
          <el-popover popper-class="cFilterListWrap" placement="bottom-end" width="600" trigger="click" v-model="popVisible">
            <div class="filterList">
              <ul>
                <li v-for="(item, index) in filterData" :key="index">
                  <filterInput :ref="`filterItem${index}`" v-bind="item" binding showAll @input="handlePopInputChange($event, item)" />
                </li>
              </ul>
              <div class="operaBtn">
                <el-button size="small" @click="resetFilter">重置</el-button>
                <el-button type="primary" size="small" @click="handleFilter">确定</el-button>
              </div>
            </div>
            <el-button type="text" slot="reference" v-if="listLayout.includes('filter')">
              展开过滤
              <i class="el-icon-arrow-down el-icon--right" />
            </el-button>
          </el-popover>
        </div>
      </el-col>
    </el-row>

    <!-- 顶部分页 -->
    <div class="listPageInfo">
      <pager
        v-bind="$attrs"
        :selectData="selectData"
        :pageInfo="pageInfo"
        :hideSelectTips="pure || !openSelection"
        @changeCurrentPage="changeCurrentPage"
        @changePageSize="changePageSize"
      />
    </div>

    <!-- 列表 -->
    <div class="listWrap">
      <slot>
        <el-table ref="multipleTable" :data="listData" @selection-change="handleSelect">
          <el-table-column type="selection" width="55" align="center" v-if="openSelection" />
          <el-table-column v-for="item in dataInfo" :key="item.prop" v-bind="item" />
          <el-table-column label="操作" align="center" class="operaColumn">
            <template slot-scope="scope">
              <el-button
                type="text"
                size="mini"
                @click="handleBtnClick(item.name,scope.row)"
                v-for="item in tableMenu"
                v-if="item.f(scope.row)"
                :key="item.name"
              >{{item.label}}</el-button>
            </template>
          </el-table-column>
        </el-table>
      </slot>
    </div>
  </div>
</template>

<script>
import filterInput from './components/filterInput'
import pager from './components/pager'
export default {
  name: 'dataList',
  components: {
    filterInput,
    pager
  },
  inject: ['initList'],
  props: {
    // 极简模式【仅保留分页和列表内容插槽，移除所有其他内容】
    pure: {
      type: Boolean,
      default: false
    },
    // 搜索参数名称
    searchName: {
      type: String,
      default: ''
    },
    // 列表数据
    listData: {
      type: Array,
      default: () => []
    },
    /**
     * 表格展示数据设置
     */
    dataInfo: {
      type: Array,
      default: () => []
    },
    /**
     * 操作菜单列表
     */
    operaInfo: {
      type: Array,
      default: () => []
    },
    // 是否需要勾选项
    openSelection: {
      type: Boolean,
      default: false
    },
    // 分页数据
    pageInfo: {
      type: Object,
      default: () => ({
        total: 0, // 总页数
        current: 1, // 当前页数
        size: 10, // 每页显示多少条
      })
    },
    // 选中的数据
    selectData: {
      type: Array,
      default: () => []
    },
    // 搜索框提示语
    placeholder: {
      type: String,
      default: '请输入内容'
    },
    /**
     * 功能按钮列表
     * 默认支持【新增(add)、删除(delete)、导入(import)、导出(export)】;
     * 如果要修改名称的话，传[{add:'新增按钮'},'delete',{export:"导入按钮"}]即可
     * 如果要新增其他按钮的话，['add','delete',{test:'测试按钮'},{test2:"测试按钮2"}]
     */
    menuList: {
      type: Array,
      default: () => ['add', 'delete']
    },
    /**
     * 筛选列表
     * label: String [必填] 标题 
     * inputType: String [默认值 input] 筛选类型
     * 【input:文本搜索框; dict: 字典筛选(下拉框展示), radio: 单选框, checkbox: 多选框, select: 下拉框, date: 日期, datetime: 时间日期, daterange: 日期区间, datetimerange: 时间日期区间】
     * multiple: Boolean [默认值 false] 是否多选，仅对type为dict或者select时生效
     * children: Array 自定义筛选项的列表 参考 [{ label:'是', value:'1'}]
     * dictName: String 字典名称 type为dict时必填
     * placeholder: String 筛选项提示
     * name: String 筛选项对应键名 当inputType类型为日期区间或者时间区间时，可以填一个数组,分别放开始时间和结束时间的键名 => ['startTime','endTime']
     * defaultValue: String|Array 让筛选项默认显示，设置默认筛选的值，多选或者时间区间类型时传数组，单选或者非区间类型传字符串
     * Example：
     * { label: '文字搜索筛选', inputType: 'input', name: 'input' },
     * { label: '字典筛选', inputType: 'dict', dictName: 'contract_type', name: 'dict'},
     * { label: '单选筛选',inputType: 'radio',name: 'radio',children: [{ label:'选项1', value:'1'},{ label:'选项2', value:'0'}] },
     * { label: '多选筛选', inputType: 'checkbox', name: 'checkbox', children: [{ label:'选项1', value:'1'},{ label:'选项2', value:'0'}] },
     * { label: '下拉筛选', inputType: 'select', name: 'select', children: [{ label:'选项1', value:'1'},{ label:'选项2', value:'0'}], multiple: true },
     * { label: '日期时间筛选', inputType: 'datetime', name: 'datetime',},
     * { label: '日期筛选', inputType: 'date', name: 'date',},
     * { label: '日期区间', inputType: 'daterange', name: ['startTime','endTime']},
     * { label: '日期时间区间', inputType: 'datetimerange', name: ['startTime','endTime']},
     * { label: '数字区间', inputType: 'numberRange', name: ['startNum','endNum']},
     */
    filterList: {
      type: Array,
      default: () => [],
    },
    /**
     * 筛选的展现形式，目前有两种，一种是默认只显示一个展开过滤的按钮，点击通过弹窗显示筛选项【默认:default】
     * 另一种是默认显示两个筛选项，右边有一个展开按钮，点击显示全部筛选项【all】
     */
    filterType: {
      type: String,
      default: 'default'
    },
    // 筛选项所占的份数（总数24，默认8），如果是弹窗筛选类型【filterType为default】,那么是设置筛选按钮所占的份数
    filterSpan: {
      type: Number,
      default: 8
    },
    // 菜单按钮区域所占的份数，默认8，最高16
    menuSpan: {
      type: Number,
      default: 8,
      validator (val) {
        return val >= 8 && val <= 16
      }
    },
    // 清除组件默认上边距
    noPadding: {
      type: Boolean,
      default: false
    },
    /**
     * 模块列表，根据场景自定义需要的模块【实验中，待完善】
     * menu: 菜单列表; search: 搜索框; filter: 数据筛选;
     */
    listLayout: {
      type: Array,
      default: () => ['menu', 'search', 'filter']
    },
  },

  data () {
    return {
      // 搜索关键词
      searchValue: '',
      // 展开搜索
      openFilter: false,
      // 条件筛选选中项
      choosedFilterList: [],
      // 开启搜索弹窗
      popVisible: false,
      // 暂存筛选参数（防止页面请求时，参数合并造成数据重置失效）
      formData: {},
      // 判断暂存筛选是否和条件筛选绑定
      ovservableNumber: 0,
    }
  },
  computed: {
    // 菜单列表
    menu () {
      const menuInfo = {
        add: '新增',
        delete: '删除',
        import: '导入',
        export: '导出',
      }
      return this.menuList.map(item => {
        if (typeof item === 'string') {
          return { label: menuInfo[item], name: item }
        } else {
          let [name, label] = Object.entries(item)[0]
          return { label, name }
        }
      })
    },
    // 初始化筛选列表
    filterData () {
      if (!this.filterList.length) return [];
      let values = new Map()
      this.filterList.forEach(item => {
        item.defaultValue && values.set(item.name, item.defaultValue)
      })
      values.size && setTimeout(() => this.setFilter(values), 0)
      return this.filterList.map((item, index) => ({ ...item, filterName: `filterItem${index}`, binding: false }))
    },
    // 判断是否显示组件顶部的搜索和菜单列表
    showListTop () {
      return !this.pure || (!this.listLayout.includes('search') && !this.listLayout.includes('menu'))
    },
    // 表格操作菜单列表
    tableMenu () {
      const menuInfo = {
        preview: '查看',
        edit: '编辑',
        delete: '删除'
      }
      let _f = () => 1;
      return this.operaInfo.map(item => {
        switch (Object.prototype.toString.call(item)) {
          case '[object String]':
            return { label: menuInfo[item], name: item, f: _f }
          case '[object Array]':
            const [name, f] = item
            return { label: menuInfo[name], name, f: f == undefined ? _f : f };
          case '[object Object]':
            let [n, label] = Object.entries(item)[0]
            return { label, name: n, f: _f }
          default:
            return ''
        }
      })
    }
  },
  watch: {
    // 左右两个筛选双向绑定
    choosedFilterList: {
      deep: true,
      handler (val, oldVal) {
        // 过滤出值改变了的ref
        let refs = val.filter(item => {
          let contrast = oldVal.find(_e => _e.filterName == item.filterName)
          return contrast == undefined || JSON.stringify(item.value) != JSON.stringify(contrast.value)
        })
        refs.forEach(item => {
          const ref = this.$refs[item.filterName][0]
          ref.setVal(item.value)
        })
      }
    }
  },
  methods: {
    // 手动设置默认筛选项
    setFilter (map = new Map()) {
      if (!map.size) return;
      let formData = {}
      let choosedFilterList = []
      map.forEach((value, key) => {
        let filterItem = this.filterData.find(_ => JSON.stringify(_.name) == JSON.stringify(key))
        //检测传值的正确性
        let isMultiple = ['datetimerange', 'daterange', 'numberRange', 'checkbox'].includes(filterItem.inputType) || filterItem.multiple
        if (isMultiple && Object.prototype.toString.call(value) !== "[object Array]") {
          console.error(`筛选项${key}默认值必须为数组类型`)
          return;
        }
        if (filterItem) {
          // 因为是预设值，所以日期和数字区间可以放在一块,把数字区间的预设值调整为数组
          if (['datetimerange', 'daterange', 'numberRange'].includes(filterItem.inputType) && Array.isArray(filterItem.name)) {
            const [startName, endName] = filterItem.name
            const [startValue, endValue] = value
            formData[startName] = startValue
            formData[endName] = endValue
          } else {
            formData[filterItem.name] = value
          }
          choosedFilterList.push({
            ...filterItem,
            inputType: ['checkbox', 'radio'].includes(filterItem.inputType) ? 'select' : filterItem.inputType,
            value: value,
            multiple: filterItem.inputType == 'checkbox' ? true : !!filterItem.multiple
          })
        }
      })
      this.formData = formData
      this.choosedFilterList = choosedFilterList
    },
    // 条件筛选
    handleFilter () {
      let formData = {}
      let choosedFilterList = []
      let validate = true;
      this.filterData.forEach((item, index) => {
        let ref = this.$refs[`filterItem${index}`][0]
        if (ref.filterValue !== '') {
          // 筛选项日期区间处理
          if (['datetimerange', 'daterange'].includes(ref.inputType) && Array.isArray(ref.name)) {
            const [startName, endName] = ref.name
            const [startTime, endTime] = ref.filterValue
            formData[startName] = startTime
            formData[endName] = endTime
          } else if (['numberRange'].includes(ref.inputType) && Array.isArray(ref.name)) {
            const { valid, form } = ref.getFormData()
            const [startName, endName] = ref.name
            const { min, max } = form
            formData[startName] = min
            formData[endName] = max
            validate = valid

          } else {
            formData[ref.name] = ref.filterValue
          }
          if (this.filterType === 'default' && ref.filterValue !== '' && JSON.stringify(ref.filterValue) != '[]' && JSON.stringify(ref.filterValue) != `{"min":"","max":""}`) {
            choosedFilterList.push({
              ...item,
              inputType: ['checkbox', 'radio'].includes(item.inputType) ? 'select' : item.inputType,
              value: ref.filterValue,
              multiple: item.inputType == 'checkbox' ? true : !!item.multiple
            })
          }
        };
      })
      // 数值区间校验
      if (!validate) return;
      // 重置逻辑处理
      Object.keys(this.formData).forEach(item => {
        if (formData[item] == undefined) {
          formData[item] = ''
        }
      })

      this.formData = formData
      this.popVisible = false
      this.choosedFilterList = choosedFilterList
      this.initList({ current: 1, ...formData })
    },
    // 筛选后二次筛选
    handleSecFilter () {
      let formData = {}
      let validate = true;
      this.choosedFilterList.forEach((item, index) => {
        let ref = this.$refs[`chooseFilterItem${index}`][0]
        console.log(ref)
        // 筛选项日期区间处理
        if (['datetimerange', 'daterange'].includes(ref.inputType) && Array.isArray(ref.name)) {
          const [startName, endName] = ref.name
          const [startTime, endTime] = ref.filterValue || []
          formData[startName] = startTime
          formData[endName] = endTime
        } else if (['numberRange'].includes(ref.inputType) && Array.isArray(ref.name)) {
          const { valid, form } = ref.getFormData()
          const [startName, endName] = ref.name
          const { min, max } = form
          formData[startName] = min
          formData[endName] = max
          validate = valid
        } else {
          formData[ref.name] = ref.filterValue
        }
      })
      // 数值区间校验
      if (!validate) return;
      // 重置逻辑处理
      Object.keys(this.formData).forEach(item => {
        if (formData[item] == undefined) {
          formData[item] = ''
        }
      })
      this.formData = formData
      this.initList({ current: 1, ...formData })
    },
    // 重置筛选
    resetFilter () {
      let formData = {}
      this.filterData.forEach((item, index) => {
        let ref = this.$refs[`filterItem${index}`][0]
        ref.clear()
        if (['datetimerange', 'daterange', 'numberRange'].includes(ref.inputType) && Array.isArray(ref.name)) {
          const [startName, endName] = ref.name
          formData[startName] = ''
          formData[endName] = ''
        } else {
          formData[ref.name] = ref.multiple || ref.inputType == 'checkbox' ? [] : ''
        }
      })
      this.popVisible = false
      this.$emit('resetFilter')
      this.initList({ current: 1, ...formData })
      this.choosedFilterList.length = 0
    },
    // 条件筛选后双向绑定
    handlePopInputChange (val, data) {
      if (!this.choosedFilterList.length) return;
      this.choosedFilterList.forEach((item, index) => {
        if (item.filterName == data.filterName) {
          const ref = this.$refs[`chooseFilterItem${index}`][0]
          ref.setVal(val)
        }
      })
    },
    // 同上
    handleFilterInputChange (val, data) {
      if (!this.filterData.length) return;
      this.filterData.forEach((item, index) => {
        if (item.filterName == data.filterName) {
          const ref = this.$refs[`filterItem${index}`][0]
          ref.setVal(val)
        }
      })
    },
    // 关键词搜索
    searchByKeywords (searchValue) {
      if (!this.searchName) {
        throw Error("搜索关键词不存在")
      } else {
        this.initList({ current: 1, [this.searchName]: searchValue })
      }
    },
    // 修改当前页
    changeCurrentPage (current) {
      this.initList({ current })
    },
    // 修改单页条数
    changePageSize (size) {
      this.initList({ current: 1, size })
    },
    // 清空数据
    handleClear () {
      this.initList({ current: 1, [this.searchName]: '' })
    },
    // 菜单按钮点击事件
    menuClick (item) {
      item.name === 'delete' && this.selectData.length == 0 ? this.$alert("请勾选要删除的数据！") : this.$emit('menu-click', item.name);
    },
    // 表格菜单点击事件
    handleBtnClick (type, row) {
      this.$emit('btn-click', { type, row })
    },
    // 监听勾选
    handleSelect (arr) {
      this.$emit('selection-change', arr)
    }
  }
}
</script>

<style lang="scss" >
.dataList {
  padding-top: 30px;
  &.smallPadding {
    padding-top: 10px;
  }
  &.noPadding {
    padding-top: 0;
  }
  .listTop {
    margin-bottom: 10px;
  }
  .listPageInfo {
    margin-top: 10px;
  }
  .searchPanel {
    .el-input {
      width: 280px;
    }
  }
  .btnList {
    text-align: right;
  }
  .flex-full {
    flex: 1;
  }
  .listFilter {
    .el-col {
      margin-top: 10px;
      display: none;
      &:nth-child(-n + 2) {
        display: block;
      }
      &:last-child {
        display: block;
      }
    }
    &.open .el-col {
      display: block !important;
    }
  }
  .chooseFilter {
    display: flex;
    ul {
      display: flex;
      flex-wrap: wrap;
      flex-grow: 1;
      li {
        padding: 0 10px;
        margin-bottom: 10px;
      }
    }
    & > span {
      flex-shrink: 0;
      align-items: flex-start;
      line-height: 40px;
    }
  }
  .cfilterRight {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    .searchPanel {
      margin-right: 10px;
    }
  }
}
</style>

<style lang="scss" scoped>
.cFilterListWrap {
  /deep/ .filterList {
    li {
      margin-bottom: 10px;
    }
  }
  .operaBtn {
    border-top: 1px solid #ebeef5;
    padding-top: 10px;
    display: flex;
    justify-content: flex-end;
  }
}
</style>